package org.mebusw.lostcities;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Random;
import org.mebusw.dao.IGameTableDAO;
import org.mebusw.dao.IUserDAO;
import org.mebusw.dao.factory.DAOFactory;
import org.mebusw.lostcities.vo.LCCard;
import org.mebusw.lostcities.vo.LCGameTable;
import org.mebusw.lostcities.vo.LCPlayer;
import org.mebusw.game.IGameManager;
import org.mebusw.vo.GameTable;
import org.mebusw.vo.Player;
import org.mebusw.vo.User;

public class GameManager implements IGameManager {
	private final static int INIT_HAND_SET = 8;
	private LCGameTable gt;

	public GameManager() {
	}

	public void initNewGame() {
		try {
			initAllCards();
			initPool();
			initDiscards();
			initPlayerHands();

			Date t = new Date();
			Random rand = new Random(t.getTime());
			int startPlayer = rand.nextInt(gt.getPlayerCount());

			gt.getPlayers().get(startPlayer).setState(PlayerState.PLAYING);
			gt.setCurrPlayer(startPlayer);

			gt.setState(GameStatus.ONGOING);
			System.out.println("LC GameManager.initNewGame():"
					+ gt.getAllCards().size() + gt.getPool().size()
					+ gt.getPlayers().size());
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	private void initDiscards() {

		/** to avoid warning for "new ArrayList[5]" */
		gt.setDiscards(new ArrayList<List<LCCard>>());

		for (int i = 0; i < LCCard.CIVILIZATIONS; i++)
			gt.getDiscards().add(new ArrayList<LCCard>());

		// //////////TODO for test
		// gt.getDiscards().get(0).add(gt.getAllCards().get(0));
		// gt.getDiscards().get(0).add(gt.getAllCards().get(1));
		// ((LCPlayer) gt.getPlayers().get(0)).getTableaus().get(0).add(
		// gt.getAllCards().get(0));
		// ((LCPlayer) gt.getPlayers().get(0)).getTableaus().get(0).add(
		// gt.getAllCards().get(1));
		// /////////////

		System.out.println("GameManager.initDiscards():"
				+ gt.getDiscards().size());

	}

	private void initPlayerHands() {

		List<LCCard> hands;
		LCCard card;

		for (int i = 0; i < gt.getPlayers().size(); i++) {
			hands = ((LCPlayer) gt.getPlayers().get(i)).getHands();
			for (int j = 0; j < INIT_HAND_SET; j++) {
				card = randTake();
				card.setOwner(i);
				hands.add(card);
			}
		}
		System.out.println("GameManager.initPlayerHands():"
				+ ((LCPlayer) gt.getPlayers().get(0)).getHands().size());

	}

	private void initPool() {

		gt.setPool(new ArrayList<LCCard>());
		for (LCCard card : gt.getAllCards()) {
			gt.getPool().add(card);
		}
		Collections.shuffle(gt.getPool());
		System.out.println("GameManager.initPool():" + gt.getPool().size());

	}

	private void initAllCards() {
		LCCard card;
		int temp_sum = 0;

		gt.setAllCards(new ArrayList<LCCard>());
		for (int civilization = 0; civilization < LCCard.CIVILIZATIONS; civilization++) {
			for (int j = 2; j <= 10; j++) {
				card = new LCCard(j, temp_sum++, LCCard.NumberCard,
						LCCard.IN_POOL, civilization);
				gt.getAllCards().add(card);
			}
			for (int k = 0; k < 3; k++) {
				card = new LCCard(0, temp_sum++, LCCard.InvestCard,
						LCCard.IN_POOL, civilization);
				gt.getAllCards().add(card);
			}
		}
		System.out.println("GameManager.initAllCards():" + temp_sum
				+ gt.getAllCards().size());
	}

	public void joinGame(String playerName, int uid) {
		System.out.println("LC-GameManager.joinGame():" + uid + playerName);
		int i;
		for (i = 0; i < gt.getPlayerCount(); i++) {

			if (gt.getPlayers().get(i).getName().equals(playerName)) {
				return;
			}
			if (gt.getPlayers().get(i).getName().equals("")) {
				gt.getPlayers().get(i).setName(playerName);
				gt.getPlayers().get(i).setUserId(uid);
				if (i == gt.getPlayerCount() - 1) {
					// that means player slots are full.
					initNewGame();
				}
				return;
			}
		}
	}

	public int playerNameToID(String username) {
		for (int i = 0; i < gt.getPlayers().size(); i++) {
			if (gt.getPlayers().get(i).getName().equals(username))
				return i;
		}
		return LCGameTable.UNKNOWN_PLAYER;

	}

	public void loadGame(int gid) {
		IGameTableDAO dao = DAOFactory.getIGameTableDAOInstance();
		try {
			System.out.println("loading game gid=" + gid);
			gt = (LCGameTable) dao.findById(gid);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void createGame(String playerName, int uid, int playerCount,
			String description, String gameName, int expansion) {
		gt = new LCGameTable();
		gt.setGameName("Lost Cities");
		System.out.println("GameManager.create():" + playerName + uid
				+ playerCount + description);

		gt.setPlayerCount(playerCount);
		gt.setDescription(description);

		gt.setPlayers(new ArrayList<Player>());
		for (int i = 0; i < gt.getPlayerCount(); i++) {
			LCPlayer p = new LCPlayer("");
			p.setState(PlayerState.WAITING);
			p.setTableaus(new ArrayList<List<LCCard>>());
			for (int j = 0; j < LCCard.CIVILIZATIONS; j++) {
				p.getTableaus().add(new ArrayList<LCCard>());
			}
			p.setHands(new ArrayList<LCCard>());
			gt.getPlayers().add(p);
		}

		gt.setCurrPlayer(0);
		gt.setState(GameStatus.NOTSTARTED);

		IGameTableDAO dao = DAOFactory.getIGameTableDAOInstance();
		try {
			System.out.println("creating game");
			dao.doCreate(gt);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// private void test() {
	// FileInputStream fis = null;
	// FileOutputStream fos = null;
	// ObjectInputStream ois = null;
	// ObjectOutputStream oos = null;
	//
	// try {
	// fos = new FileOutputStream("./S1.bin");
	// ByteArrayOutputStream baos = new ByteArrayOutputStream();
	// oos = new ObjectOutputStream(fos);
	// // oos = new ObjectOutputStream(baos);
	//
	// oos.writeObject(gt);
	//
	// ByteArrayInputStream bais = new ByteArrayInputStream(baos
	// .toByteArray());
	// System.out.println("size of baos: " + baos.size());
	//
	// System.out.println("available of bais: " + bais.available());
	// oos.close();
	//
	// fis = new FileInputStream("./S1.bin");
	//
	// ois = new ObjectInputStream(fis);
	// // ois = new ObjectInputStream(bais);
	// System.out.println("available of ois: " + ois.available());
	// LCGameTable gt1 = (LCGameTable) ois.readObject();
	// ois.close();
	//
	// // while((a=fis.read())!=-1){
	// // fos.write(a);
	// // }
	// } catch (Exception e) {
	// e.printStackTrace();
	// } finally {
	// }
	// }

	public void saveGame() {
		IGameTableDAO dao = DAOFactory.getIGameTableDAOInstance();
		try {
			System.out.println("saving game gid=" + gt.getGid());
			dao.doUpdate(gt);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public GameTable getGt() {
		return gt;
	}

	/**
	 * When card pool is empty, the player with highest score is the winner,
	 * game over.
	 * 
	 * @return the player ID of the winner
	 */

	private int checkWin() {
		int scores[] = new int[2];
		int winner = -1;
		if (0 == gt.getPool().size()) {
			scores[0] = gt.getPlayers().get(0).getVictoryPoint();
			scores[1] = gt.getPlayers().get(1).getVictoryPoint();
			gt.setState(GameStatus.GAMEOVER);
			gt.getPlayers().get(0).setState(PlayerState.GAMEOVER);
			gt.getPlayers().get(1).setState(PlayerState.GAMEOVER);
			if (scores[0] != scores[1])
				winner = (scores[0] > scores[1] ? 0 : 1);
			updateStatistics(winner);
			gt.setCurrPlayer(winner);
		}
		return winner;
	}

	private int calcScore(int playerId) {
		int score = 0;
		int scoreOfOneCivilization;
		int factor;
		List<List<LCCard>> tableaus = ((LCPlayer) gt.getPlayers().get(playerId))
				.getTableaus();

		for (int j = 0; j < LCGameTable.CATEGORY_COUNT; j++) {
			List<LCCard> tableau = tableaus.get(j);
			if (0 == tableau.size())
				continue;
			scoreOfOneCivilization = -20;
			factor = 1;
			for (LCCard card : tableau) {
				scoreOfOneCivilization += card.getValue();
				if (card.isInvestCard())
					factor++;
			}
			scoreOfOneCivilization *= factor;
			if (tableau.size() >= 8)
				scoreOfOneCivilization += 20;
			score += scoreOfOneCivilization;

		}
		return score;
	}

	private void updateStatistics(int winner) {
		User user;
		IUserDAO dao;

		System.out.println("updating Statistics gid=" + gt.getGid());
		for (int i = 0; i < gt.getPlayers().size(); i++) {
			dao = DAOFactory.getIUserDAOInstance();
			try {
				user = dao.findByName(gt.getPlayers().get(i).getName());
				user.setPlayed_game_lc(user.getPlayed_game_lc() + 1);
				if (winner == i)
					user.setWin_game_lc(user.getWin_game_lc() + 1);
				dao = DAOFactory.getIUserDAOInstance();
				try {
					dao.doUpdate(user);
				} catch (Exception e) {
					e.printStackTrace();
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public boolean onTakeACard(String playerName, int cardToTake) {
		System.out.println("onTakeACard() " + playerName + cardToTake);
		boolean flag = false;
		try {
			int playerId = playerNameToID(playerName);
			LCCard card;
			List<LCCard> hand = ((LCPlayer) gt.getPlayers().get(playerId))
					.getHands();

			if (-1 == cardToTake && gt.getPool().size() > 0) {
				/** take from pool */
				card = gt.getPool().get(gt.getPool().size() - 1);
				if (LCCard.IN_POOL == card.getOwner()) {
					hand.add(card);
					card.setOwner(playerId);
					gt.getPool().remove(card);
					flag = true;
				}

			} else if (cardToTake >= 0) {
				/** take the specific card from discards */
				card = gt.getAllCards().get(cardToTake);
				if (LCCard.IN_DISCARD == card.getOwner()) {
					List<LCCard> discard = gt.getDiscards().get(
							card.getCivilization());
					hand.add(card);
					card.setOwner(playerId);
					discard.remove(card);
					flag = true;
				}
			}
			gt.getPlayers().get(playerId).setState(PlayerState.WAITING);
			gt.getPlayers().get(nextPlayer(playerId)).setState(
					PlayerState.PLAYING);

			checkWin();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return flag;
	}

	private int nextPlayer(int currPlayerId) {
		return (currPlayerId + 1) % gt.getPlayerCount();
	}

	public boolean onPlayACard(String playerName, int cardToPlay, int target) {
		System.out.println("onPlayACard() " + playerName + cardToPlay + target);
		boolean flag = false;
		try {
			if (-1 == cardToPlay)
				return false;
			int playerId = playerNameToID(playerName);
			LCCard card = gt.getAllCards().get(cardToPlay);
			System.out.println("onPlayACard():card= " + card.getCardId()
					+ card.getCivilization() + card.getValue());

			List<LCCard> hand = ((LCPlayer) gt.getPlayers().get(playerId))
					.getHands();

			if (1 == target
					&& (card.getOwner() >= 0 && card.getOwner() < gt
							.getPlayerCount())) {
				/** play to own tableau */
				List<LCCard> tableau = ((LCPlayer) gt.getPlayers()
						.get(playerId)).getTableaus().get(
						card.getCivilization());
				if (0 == tableau.size()
						|| tableau.get(tableau.size() - 1).getValue() <= card
								.getValue()) {
					tableau.add(card);
					hand.remove(card);
					gt.getPlayers().get(playerId).setState(PlayerState.TAKING);
					flag = true;
				}
			} else if (0 == target
					&& (card.getOwner() >= 0 && card.getOwner() < gt
							.getPlayerCount())) {
				/** discard it */
				List<LCCard> discard = gt.getDiscards().get(
						card.getCivilization());
				discard.add(0, card);
				card.setOwner(LCCard.IN_DISCARD);
				hand.remove(card);
				gt.getPlayers().get(playerId).setState(PlayerState.TAKING);
				flag = true;
			}
			gt.getPlayers().get(playerId).setVictoryPoint(calcScore(playerId));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return flag;

	}

	public void setGt(GameTable gt) {
		this.gt = (LCGameTable) gt;

	}

	public LCCard randTake() {

		LCCard card = null;
		Random rand = new Random(System.currentTimeMillis());

		if (gt.getPool().size() <= 0)
			return null;
		try {
			int index = rand.nextInt(gt.getPool().size());
			card = (LCCard) gt.getPool().get(index);
			gt.getPool().remove(index);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("LC-GameManager.randTake(): " + card);
		return card;
	}

	public static void main(String[] args) {

		System.out.println("hello");

		GameManager gm = new GameManager();
		gm.createGame("mebusw", 0, 2, "a", "Lost Cities", 0);

	}

	public GameTable getGtVO() {
		return this.getGt();
	}

}
